/**
 * Copyright (c) 2009 blurredVision
 * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and
 * associated documentation files (the "Software"), to deal in the Software without restriction, 
 * including without limitation the rights to use, copy, modify, merge, publish, distribute, 
 * sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is 
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all copies or 
 * substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT 
 * NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 
 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, 
 * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT 
 * OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 */
#include "StdAfx.h"
#include "JukeBox_OpenAL.h"

namespace openAL {

JukeBox_OpenAL::JukeBox_OpenAL(ALuint _sourceID) :
	m_CurrentSong(NULL),

	m_iNumOfSongs(0),
	m_iCurrentSong(0),
	m_iLoopTrack(-1),
	m_iBaseLoopCycle(1),
	m_iNumberOfBaseLoopRepeats(1),

	m_bShouldBePlaying(false),
	
	m_fFadeFactor(0),
	m_fDeltaFade(0),

	m_iFreeBuffers(NR_JB_BUFFERS),
	m_iNextFreeBuffer(0),

	m_sourceID(_sourceID)
{
	// Generate some AL Buffers for streaming
	alGenBuffers(NR_JB_BUFFERS, m_aBuffers);		
}

JukeBox_OpenAL::~JukeBox_OpenAL(void)
{
	alDeleteSources(1, &m_sourceID);

	//TODO destroy playlist
}


void JukeBox_OpenAL::play() {
	
	if (m_bShouldBePlaying) return;

	m_fFadeFactor = 1;
	m_fDeltaFade = 0;
	
	fillBuffersAndPlay();
}

void JukeBox_OpenAL::play_fadeIn(float fFadeTime) {
	
	if (m_bShouldBePlaying) return;
	
	m_fFadeFactor = 0.0001f;
	m_fDeltaFade = 1/fFadeTime;

	fillBuffersAndPlay();
}

void JukeBox_OpenAL::stop() {
	
	// Stop source
	alSourceStop(m_sourceID);

	m_bShouldBePlaying = false;

	m_iNextFreeBuffer = 0;
		
	if (m_CurrentSong) {
		// Close buffer
		m_CurrentSong->pBuffer->close();
	
		// Reset Currentsong
		m_CurrentSong = NULL;
	}
}

void JukeBox_OpenAL::fadeAndStop(float fFadeTime) {
	m_fDeltaFade = -1/fFadeTime;	
}

void JukeBox_OpenAL::nextSong(float _fadeTime) {

}

void JukeBox_OpenAL::previousSong(float _fadeTime) {

}
	
int JukeBox_OpenAL::addToPlaylist(const std::string &_fileName) {
	
	// Check if songs is already in the playlist
	SongMap::iterator it;
	it = m_mPlaylist.find(_fileName);
	if (it != m_mPlaylist.end()) {
		return true;
	}

	// Create new song and at to playlist
	Song* pS = new Song();
	pS->ordernr = m_iNumOfSongs;
	pS->sFilename = _fileName;
	pS->pBuffer = new StreamingBuffer_OpenAL(_fileName);
	
	m_mPlaylist[_fileName] = pS;

	if (!m_CurrentSong) {
		m_CurrentSong = pS;
	}

	++m_iNumOfSongs;
	return pS->ordernr;
}
	
void JukeBox_OpenAL::removeFromPlaylist(const std::string &_fileName) {

}
	
void JukeBox_OpenAL::setVolume(float _vol) {
	m_fVolume = _vol;
}

void JukeBox_OpenAL::setCurrentSong(int _number) {
	m_iCurrentSong = _number;
}

void JukeBox_OpenAL::setCurrentSong(const std::string &_filename) {
	SongMap::iterator it;
	it = m_mPlaylist.find(_filename);
	if (it != m_mPlaylist.end()) {
		m_iCurrentSong = it->second->ordernr;		
	}
}

int JukeBox_OpenAL::getCurrentSong() {
	return m_iCurrentSong;
}

void JukeBox_OpenAL::fillBuffersAndPlay() {
		
	if (!m_CurrentSong || (m_CurrentSong->ordernr != m_iCurrentSong)) {
		m_CurrentSong = getSongByNumber(m_iCurrentSong);
		if (!m_CurrentSong) {
			coreGetLog()->WriteLog("Cannot start JukeBox! Playlist is empty!",__FILE__,__LINE__);		
		}
	}
	
	// Errorhandling
	alGetError();
	ALenum error = AL_NO_ERROR;	
	
	// Open the Streamingbuffer
	m_CurrentSong->pBuffer->open();
	
	// Fill buffers and queue to source
	bool bHasMoreSamples;
	for (int i = 0; i < NR_JB_BUFFERS; ++i) {
		bHasMoreSamples = m_CurrentSong->pBuffer->fillBuffer(m_aBuffers[i]);
		if (bHasMoreSamples) {
			alSourceQueueBuffers(m_sourceID, 1, &m_aBuffers[i]);			
			error = alGetError();
			if (error != AL_NO_ERROR) {
				coreGetLog()->WriteError("Cannot queue Buffer!",__FILE__,__LINE__);		
			} else {
				++m_iNextFreeBuffer;
				if (m_iNextFreeBuffer >= NR_JB_BUFFERS) {
					m_iNextFreeBuffer = 0;
				}
				--m_iFreeBuffers;
			}		
		} else {			
			break;
		}
	}

	// Set volume
	float sfxVol = coreGetAudio()->getMusicVolume();
	sfxVol *= m_fVolume;
	sfxVol *= m_fFadeFactor;
	alSourcef(m_sourceID, AL_GAIN, sfxVol);
		
	alSourcePlay(m_sourceID);

	m_bShouldBePlaying = true;
}

JukeBox_OpenAL::Song* JukeBox_OpenAL::getSongByNumber(unsigned int _nr) {
	SongMap::iterator it;
	it = m_mPlaylist.begin();
	for (it; it != m_mPlaylist.end(); ++it) {
		if (it->second->ordernr == _nr) {
			return it->second;
		}
	}
	return NULL;
}

void JukeBox_OpenAL::setIntroAndLoop(unsigned int _intro, unsigned int _loop) {
	m_iCurrentSong = _intro;
	m_iLoopTrack = _loop;
}


void JukeBox_OpenAL::update(const double &dt) {
		
	if (m_bShouldBePlaying) {
		// Check if JukeBox is still playing
		ALint	iState;		
		alGetSourcei(m_sourceID, AL_SOURCE_STATE, &iState);
		if (iState != AL_PLAYING) alSourcePlay(m_sourceID);
	}
	
	
	if (m_fFadeFactor == 0) {
		stop();
	}
	
	// volume
	float sfxVol = coreGetAudio()->getMusicVolume();
	sfxVol *= m_fVolume;		
	
	// Calc fades (In and Out)
	if (m_fDeltaFade != 0) {		
		m_fFadeFactor += m_fDeltaFade * (float)dt; 
	
		// FadeIn ended
		if (m_fFadeFactor >= 1) {
			m_fFadeFactor = 1;
			m_fDeltaFade = 0;
		}		
		
		// FadeOut ended
		if (m_fFadeFactor <= 0) {
			m_fFadeFactor = 0;
			m_fDeltaFade = 0;
			
		}				
	}

	sfxVol *= m_fFadeFactor;
	alSourcef( m_sourceID, AL_GAIN, sfxVol);

	// Request the number of OpenAL Buffers that have been processed (played) on the Source
	int iBuffersProcessed = 0;
	alGetSourcei(m_sourceID, AL_BUFFERS_PROCESSED, &iBuffersProcessed);

	// Remove processed buffers from queue	
	while (iBuffersProcessed) {
		
		// Remove the Buffer from the Queue.  (uiBuffer contains the Buffer ID for the unqueued Buffer)
		ALuint uiBuffer = 0;
		alSourceUnqueueBuffers(m_sourceID, 1, &uiBuffer);

		++m_iFreeBuffers;
		--iBuffersProcessed;
	}

	// Return if there is no new audio data.
	if (!m_CurrentSong) return;

	// Read the next chunk of audio data to free buffers and queue them
	bool bHasMoreSamples = true;	
	while (m_iFreeBuffers && bHasMoreSamples) {
		
		// Read more audio data
		bHasMoreSamples = m_CurrentSong->pBuffer->fillBuffer(m_aBuffers[m_iNextFreeBuffer]);		
		
		if (bHasMoreSamples) {			
			
			// Clear errors
			alGetError();
			
			// Queue buffer to source 
			alSourceQueueBuffers(m_sourceID, 1, &m_aBuffers[m_iNextFreeBuffer]);
		
			if (alGetError() != AL_NO_ERROR) {
				coreGetLog()->WriteError("Cannot queue Buffer!",__FILE__,__LINE__);		
			} else {
				--m_iFreeBuffers;
				++m_iNextFreeBuffer;
				if (m_iNextFreeBuffer >= NR_JB_BUFFERS) {
					m_iNextFreeBuffer = 0;
				}
			}		
		}		
	}

	if (!bHasMoreSamples) {
		m_CurrentSong->pBuffer->close();		
		
		switch(m_iPlaymode) {
		
			case audio::JB_NORMAL:				
				++m_iCurrentSong;
				if (m_iCurrentSong < m_iNumOfSongs) {
					m_CurrentSong = getSongByNumber(m_iCurrentSong);
					if (m_CurrentSong)
						m_CurrentSong->pBuffer->open();	
				} else {
					m_iCurrentSong = 0;
					m_CurrentSong = NULL;
				}			
			break;

			case audio::JB_PLAY_SINGLE:
				m_CurrentSong = NULL;
			break;

			case audio::JB_LOOP_ONE:
				m_CurrentSong->pBuffer->open();
			break;

			case audio::JB_LOOP_PLAYLIST:
				++m_iCurrentSong;
				
				// Do the loop thing (start from beginning)
				if (m_iCurrentSong >= m_iNumOfSongs) {
					m_iCurrentSong = 0;	
				} 

				m_CurrentSong = getSongByNumber(m_iCurrentSong);
				
				if (m_CurrentSong)
					m_CurrentSong->pBuffer->open();
			break;

			case audio::JB_INTRO_LOOP:
				
				if (m_iLoopTrack != -1) {
					m_iCurrentSong = m_iLoopTrack;
				} else {				
					++m_iCurrentSong;
				}

				if (m_iCurrentSong < m_iNumOfSongs) {
					
					m_CurrentSong = getSongByNumber(m_iCurrentSong);				
					if (m_CurrentSong)
						m_CurrentSong->pBuffer->open();					
					}

					// Now switch to loop mode
					m_iPlaymode = audio::JB_LOOP_ONE;
			break;
			
			case audio::JB_LOOP_VARIATIONS:
				if (m_iBaseLoopCycle < m_iNumberOfBaseLoopRepeats) {
					++m_iBaseLoopCycle;
					m_CurrentSong = getSongByNumber(0);
				} else {
					m_iBaseLoopCycle = 1;
				
					int next =	1 + ( rand() % (m_iNumOfSongs - 1));
					m_CurrentSong = getSongByNumber(next);											
				}
				if (m_CurrentSong)
					m_CurrentSong->pBuffer->open();
			break;
		}		
	}
	if (!m_CurrentSong)
		m_bShouldBePlaying = false;
}


void JukeBox_OpenAL::setUpLoopVariations(unsigned int _blRepeat) {
	m_iNumberOfBaseLoopRepeats = _blRepeat;
}



}
